package io.jsen.meta.diff.support.pamd;

import com.google.common.collect.Maps;
import com.google.gson.Gson;
import io.jsen.meta.diff.annotation.empty.IgnoreConvert;
import io.jsen.meta.diff.annotation.empty.IgnoreIndexFeature;
import io.jsen.meta.diff.annotation.parser.CoAnnotation;
import io.jsen.meta.diff.annotation.parser.CoConvert;
import io.jsen.meta.diff.feature.IndexFeature;
import io.jsen.meta.diff.obj.Convert;
import io.jsen.meta.diff.utils.FileUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * @author jsen
 * @date 2021/8/10
 */
public class PamdAutoLoader {

    private static final Map<Field, CoAnnotation> PAMD_ANNOTATION_MAP = Maps.newHashMap();
    private static final Map<Field, CoConvert> PAMD_CONVERT_MAP = Maps.newHashMap();

    public static void loadPamd(InputStream is) {
        String data;
        try {
            data = FileUtil.readAsString(is);
        } catch (IOException e) {
            throw new RuntimeException("pamd parse exception", e);
        }
        Gson gson = new Gson();
        PamdSchema schema = gson.fromJson(data, PamdSchema.class);
        try {
            parsePamd(schema.getPamd());
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("pamd parse exception, NoSuchFieldException", e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("pamd parse exception, ClassNotFoundException", e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("pamd parse exception, IllegalAccessException", e);
        } catch (InstantiationException e) {
            throw new RuntimeException("pamd parse exception, InstantiationException", e);
        }
    }

    private static void parsePamd(List<PamdV1> pamdV1List)
            throws ClassNotFoundException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        if (pamdV1List == null) {
            return;
        }
        for (PamdV1 pamdV1 : pamdV1List) {
            Class<?> clazz = Class.forName(pamdV1.getClazz());
            parsePamdField(clazz, pamdV1.getFields());
        }
    }

    @SuppressWarnings("all")
    private static void parsePamdField(Class<?> clazz, List<PamdV1Field> pamdV1FieldList)
            throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (pamdV1FieldList == null) {
            return;
        }
        for (PamdV1Field pamdV1Field : pamdV1FieldList) {
            Field field = clazz.getDeclaredField(pamdV1Field.getName());

            Class indexFeature;
            if (StringUtils.isNotBlank(pamdV1Field.getIndexFeature())) {
                indexFeature = Class.forName(pamdV1Field.getIndexFeature());
                if (!IndexFeature.class.isAssignableFrom(indexFeature)) {
                    throw new RuntimeException("indexFeature 不是 IndexFeature的子类");
                }
            } else {
                indexFeature = IgnoreIndexFeature.class;
            }


            CoAnnotation annotation = new CoAnnotation(pamdV1Field.isIgnore(), pamdV1Field.isUniqueKey(),
                    pamdV1Field.getAlias(), (Class<? extends IndexFeature>) indexFeature);
            PAMD_ANNOTATION_MAP.put(field, annotation);

            // convert
            Class metaConvert, nativeConvert;
            if (StringUtils.isNotBlank(pamdV1Field.getMetaConvert())) {
                metaConvert = Class.forName(pamdV1Field.getMetaConvert());
                if (!Convert.class.isAssignableFrom(metaConvert)) {
                    throw new RuntimeException("metaConvert 不是 Convert的子类");
                }
            } else {
                metaConvert = IgnoreConvert.class;
            }
            if (StringUtils.isNotBlank(pamdV1Field.getNativeConvert())) {
                nativeConvert = Class.forName(pamdV1Field.getNativeConvert());
                if (!Convert.class.isAssignableFrom(nativeConvert)) {
                    throw new RuntimeException("nativeConvert 不是 Convert的子类");
                }
            } else {
                nativeConvert = IgnoreConvert.class;
            }
            CoConvert convert = new CoConvert((Convert) nativeConvert.newInstance(),
                    (Convert) metaConvert.newInstance());
            PAMD_CONVERT_MAP.put(field, convert);
        }
    }

    public static Optional<CoAnnotation> getCoAnnotation(Field field) {
        return Optional.ofNullable(PAMD_ANNOTATION_MAP.get(field));
    }

    public static Optional<CoConvert> getCoConvert(Field field) {
        return Optional.ofNullable(PAMD_CONVERT_MAP.get(field));
    }
}
